<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="author" content="毛静文,Momo">
  <meta name="keywords" content="无差别提取视频的工具,毛静文的博客,Momo's Blog">
  <meta name="description" content="无差别提取视频的工具,网页上能播放的视频,绝大部分都可以通过本工具进行提取,无视媒体加密技术,直接从底层捕获视频资源">
  <title>Media Source Player</title>
  <style>
  /*全局设置*/
  html, body, #m-app {
    margin: 0;
    padding: 0;
  }
  body::-webkit-scrollbar { display: none}
  p {
    margin: 0;
  }
  [v-cloak] {
    display: none;
  }
  .background {
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-image: url();
    background-position: center center;
    background-size: cover;
    filter: blur(1px);
    z-index: -1;
  }
  /*工具栏*/
  .m-p-action {
    margin: 20px auto;
    max-width: 1100px;
    width: 100%;
    font-size: 35px;
    text-align: center;
    font-weight: bold;
  }
  .m-p-other, .m-p-github, .rate-group, .m-p-copy, .m-p-extract, .m-p-combining, .m-p-download {
    position: fixed;
    right: 50px;
    background-color: #eff3f6;
    background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
    color: #24292e;
    border: 1px solid rgba(27, 31, 35, .2);
    border-radius: 3px;
    cursor: pointer;
    display: inline-block;
    font-size: 14px;
    font-weight: 600;
    line-height: 20px;
    padding: 6px 12px;
    z-index: 99;
    box-shadow: 0 5px 18px 0 rgba(0, 0, 0, 0.3);
  }
  .m-p-help {
    position: fixed;
    right: 50px;
    top: 50px;
    width: 30px;
    height: 30px;
    color: #666666;
    z-index: 2;
    text-align: center;
    line-height: 30px;
    font-weight: bolder;
    border-radius: 50%;
    border: 1px solid rgba(27, 31, 35, .2);
    cursor: pointer;
    box-shadow: 0 5px 18px 0 rgba(0, 0, 0, 0.3);
    background-color: #eff3f6;
    background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
  }
  .m-p-github:hover, .m-p-help:hover{
    opacity: 0.9;
  }
  .m-p-github {
    bottom: 230px;
  }
  .m-p-other {
    bottom: 150px;
  }
  .m-p-copy {
    bottom: 110px;
  }
  .m-p-combining {
    bottom: 70px;
  }
  .m-p-extract {
    bottom: 30px;
  }
  .m-p-download {
    bottom: 190px;
  }
  .rate-group {
    top: 100px;
    right: 0;
    width: 40px;
  }
  .rate-group .select {
    color: white;
    text-align: center;
    border-radius: 2px;
    background-color: #27ae60;
  }
  /*输入框*/
  #input-box {
    position: absolute;
    top: 40%;
    left: 50%;
    width: 450px;
    transform: translate(-50%, -50%);
    
  }
  #input-box input {
    position: fixed;
    left: -4000px;
    z-index: -2;
    border-radius: 4px;
    
  }
  #input-box label {
    margin-bottom: 10px;
    padding: 0 20px;
    display: block;
    width: 100%;
    height: 40px;
    color: white;
    font-size: 20px;
    text-align: center;
    line-height: 40px;
    border-radius: 4px;
    background-color: #3D8AC7;
    opacity: 1;
    user-select: none;
    cursor: pointer;
    transition: 0.3s all;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    box-sizing: border-box;
    transition-duration: .4s;
    transition-property: background, box-shadow;
  }
  #input-box label:hover {
    opacity: 0.7!important;
    border-color: transparent;
    box-shadow: 0 5px 18px 0 rgba(0, 0, 0, 0.3);
  }
  #input-box .finish {
    background-color: #27ae60;
    cursor: auto;
  }
  #input-box .finish:hover {
    opacity: 1;
  }
  #input-box .disable {
    background-color: #bdc3c7;
    cursor: auto;
  }
  #input-box .disable:hover {
    opacity: 1;
  }
  #input-box div {
    cursor: pointer;
    color: #666666;
  }
  /*视频区*/
  video {
    position: absolute;
    top: 100px;
    left: 50%;
    width: calc(100% - 130px);
    transform: translateX(-50%);
  }
  </style>
</head>

<body>
<div id="loading">
  页面加载中，请耐心等待...
  <h1 style="white-space: pre;">
    推荐一个无差别提取视频的工具，
    网页上能播放的视频，绝大部分都可以通过本工具进行提取，
    无视媒体加密技术，直接从底层捕获视频资源，
    工具链接：https://blog.luckly-mjw.cn/tool-show/media-source-extract/player/player.html
    <img src="https://upyun.luckly-mjw.cn/Assets/blog/media-source-extract-121-75.png" alt="无差别视频下载工具" title="logo"/>
    <a target="_blank" href="https://blog.luckly-mjw.cn/tool-show/media-source-extract/player/player.html">点击跳转</a>
  </h1>
</div>
<section id="m-app" v-cloak>
  <div class="background"></div>
  <section class="m-p-action g-box">Media Source 播放器</section>
  <template v-if="!isPlay">
    <a class="m-p-help" target="_blank" href="https://segmentfault.com/a/1190000025182822">?</a>
    <a class="m-p-github" target="_blank" href="https://github.com/Momo707577045/media-source-extract">github</a>
    <a class="m-p-combining" target="_blank" href="https://segmentfault.com/a/1190000025182822">音视频合成</a>
    <a class="m-p-other" target="_blank" href="http://blog.luckly-mjw.cn/tool-show/index.html">其他实用工具</a>
    <div class="m-p-copy" @click="copy">复制工具代码</div>
    <a class="m-p-extract" target="_blank"
      href="http://blog.luckly-mjw.cn/tool-show/media-source-extract/example/index.html">提取示例</a>
    <a class="m-p-download" target="_blank" href="http://upyun.luckly-mjw.cn/lib/player-offline.html">离线专属播放器下载</a>
  </template>

  <!--倍速设置器-->
  <div class="rate-group" v-else>
    <div v-for="(rate, index) in playbackRateList" @click="setRate(index)" :class="[ playbackRateIndex === index ? 'select' : '']">{{rate}}</div>
  </div>

  <!--文件上传-->
  <section id="input-box" v-show="!isPlay">
    <label for="video" :title="videoFileName" :class="[videoFileName ? 'finish' : '']">{{ videoFileName ? `视频已上传(${videoFileName})` : '点击，上传视频文件' }}</label>
    <label for="audio" :title="audioFileName" :class="[audioFileName ? 'finish' : '']">{{ audioFileName ? `音频已上传(${audioFileName})` : '点击，上传音频文件' }}</label>
    <input v-if="!videoFileName" id="video" accept="video/*" type="file" value="点击，上传视频文件" @change="!videoFileName && loadSource($event, 'video')">
    <input v-if="!audioFileName" id="audio" accept="audio/*" type="file" value="点击，上传音频文件" @change="!audioFileName && loadSource($event, 'audio')">
    <label @click="(audioFileName || videoFileName) && play()" :class="[audioFileName || videoFileName ? '' : 'disable']">播放</label>
    <div @click="downloadFile('http://upyun.luckly-mjw.cn/Assets/media-source/video.mp4', 'video.mp4')">点击下载示例视频</div>
    <div @click="downloadFile('http://upyun.luckly-mjw.cn/Assets/media-source/audio.mp4', 'audio.mp4')">点击下载示例音频</div>
  </section>

  <!--播放视频-->
  <video controls v-show="isPlay" ref="player"></video>

</section>
</body>
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?1f12b0865d866ae1b93514870d93ce89";
  var s = document.getElementsByTagName("script")[0];
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script src="mp4box.all.js?v=1"></script>
<script src="//upyun.luckly-mjw.cn/lib/vue.js"></script>
<script>
document.getElementById('loading') && document.getElementById('loading').remove()
new Vue({
  el: '#m-app',

  data() {
    return {
      playbackRateIndex: 2, // 播放倍度索引
      playbackRateList: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3], // 播放倍度
      isPlay: false, // 是否输入完资料，进行播放
      mediaSource: null,
      videoFileName: '',
      audioFileName: '',
      videoSourceBuffer: null,
      audioSourceBuffer: null,
      videoArrayBuffer: null,
      audioArrayBuffer: null,
    }
  },

  mounted() {
    this.mediaSource = new MediaSource
    this.$refs.player.src = URL.createObjectURL(this.mediaSource)
  },

  methods: {
    // 上传资源
    loadSource(event, type) {
      const file = event.target.files[0]
      this[`${type}FileName`] = file.name
      let reader = new FileReader();
      reader.onloadend = (loadEvent) => {
        this[`${type}ArrayBuffer`] = loadEvent.target.result
        this.getMime(this[`${type}ArrayBuffer`]).then((mime) => {
          mime = mime.replace(/,rtp/g, '')
          console.log(this[`${type}ArrayBuffer`], mime, MediaSource.isTypeSupported(mime))
          this[`${type}SourceBuffer`] = this.mediaSource.addSourceBuffer(mime)
        })
      }
      reader.readAsArrayBuffer(file);
    },

    // 获取媒体格式
    getMime(arrayBuffer) {
      return new Promise((resolve) => {
        const mp4boxfile = MP4Box.createFile()
        arrayBuffer.fileStart = 0
        mp4boxfile.onReady = (info) => resolve(info.mime)
        mp4boxfile.appendBuffer(arrayBuffer)
      })
    },

    // 播放
    play() {
      this.isPlay = true
      this.videoSourceBuffer && this.videoArrayBuffer && this.videoSourceBuffer.appendBuffer(this.videoArrayBuffer)
      this.audioSourceBuffer && this.audioArrayBuffer && this.audioSourceBuffer.appendBuffer(this.audioArrayBuffer)
    },

    // 设置播放倍数
    setRate(index) {
      this.playbackRateIndex = index
      this.$refs.player.playbackRate = this.playbackRateList[index]
    },

    // 下载示例文件
    downloadFile(url, fileName) {
      fetch(url, { mode: 'cors' })
        .then(res => res.blob())
        .then(blob => {
          const a = document.createElement('a')
          a.download = fileName
          a.href = window.URL.createObjectURL(blob)
          a.style.display = 'none'
          document.body.appendChild(a)
          a.click()
          a.remove()
        })
    },

    // 复制到剪切板
    copyToClipboard(content) {
      let $input = document.createElement('textarea')
      $input.style.opacity = '0'
      $input.value = content
      document.body.appendChild($input)
      $input.select()

      document.execCommand('copy')
      document.body.removeChild($input)
      $input = null
    },

    // 复制工具代码
    copy() {
      fetch('http://blog.luckly-mjw.cn/tool-show/media-source-extract/extract-code.js', { mode: 'cors' })
        .then(res => res.text())
        .then(text => {
          this.copyToClipboard(text)
          alert('代码复制成功')
        })
    }
  }
})

</script>

</html>

